package com.lcy.struct.bTreePlus.disk.file;

import com.lcy.struct.bTreePlus.property.BPlusFileInfo;
import com.lcy.struct.bTreePlus.disk.TestTree;
import lombok.SneakyThrows;

import java.io.*;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.charset.StandardCharsets;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.util.*;

/**
 * @author lucongyang
 * @date Created in  13:45
 * @description
 */
public class FileUtil {
    public static FileUtil getInstance() {
        return Singleton.SINGLE_TON;
    }

    public byte[] read(String url, long position, long size) {
        Path path = Paths.get(url);
        FileChannel fileChannel = null;
        try {
            fileChannel = FileChannel.open(path);
            byte[] read = read(position, size, fileChannel);
            fileChannel.close();
            return read;
        } catch (IOException e) {
            e.printStackTrace();
            throw new RuntimeException("读取错误");
        } finally {
            try {
                if (fileChannel != null) {
                    fileChannel.close();
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    @SneakyThrows
    public byte[] read(long position, long size, FileChannel fileChannel) {
        long realSize = Math.min(fileChannel.size() - position, size);
        if (realSize <= 0) {
            return new byte[0];
        }
        MappedByteBuffer mappedByteBuffer = fileChannel.map(FileChannel.MapMode.READ_ONLY, position, realSize);
        byte[] bytes = new byte[(int) realSize];
        mappedByteBuffer.get(bytes);
        return bytes;
    }

    public byte[] read(long position, long size) {
        return read(TestTree.url, position, size);
    }

    @SneakyThrows
    public void write(String url, String content) {
        ByteBuffer buffer = StandardCharsets.UTF_8.encode(CharBuffer.wrap(content));
        Path path = Paths.get(url);
        FileChannel fileChannel = FileChannel.open(path, StandardOpenOption.READ, StandardOpenOption.WRITE, StandardOpenOption.TRUNCATE_EXISTING);
        MappedByteBuffer mappedByteBuffer = fileChannel.map(FileChannel.MapMode.READ_WRITE, 10, 10);
        mappedByteBuffer.put(0, (byte) 'U');
    }

    /**
     * 参数一: FileChannel.MapMode.READ_WRITE 采用的是读写模式
     * 参数二: 0 可以直接修改的起始位置
     * 参数三: 5 是映射内存大小(不是索引位置)，即将jym.txt的多少个字节映射到内存
     * 可以直接修改的范围就是 0-5
     */
    private List<Long> rewrite(String url, long position, long size, boolean force, byte[] contentBytes) {
//        return modify(url, position, size, force, contentBytes);
        RandomAccessFile randomAccessFile = null;
        try {
            randomAccessFile = new RandomAccessFile(url, "rw");
        } catch (FileNotFoundException e) {
            throw new RuntimeException("写文件出错");
        }
        FileChannel channel = randomAccessFile.getChannel();
        List<Long> list = rewrite(position, size, force, contentBytes, channel);
        try {
            randomAccessFile.close();
        } catch (IOException e) {
            throw new RuntimeException("写文件出错");
        }
        return list;
    }

    public List<Long> rewrite(long position, long size, boolean force, byte[] contentBytes, FileChannel channel) {
        List<Long> extentPosition = new ArrayList<>();
        int length = contentBytes.length;
        int n = (int) Math.ceil(length / (size + 0.));
        for (int i = 0; i < n; i++) {
            long realPosition = i == 0 ? position : BPlusFileInfo.allocate((int) size);
            byte[] insertBytes = new byte[(int) size];
            for (int k = 0; k < size; k++) {
                int index = (int) (i * size + k);
                insertBytes[k] = length > index ? contentBytes[index] : 32;
            }
            try {
                MappedByteBuffer mappedByteBuffer = channel.map(FileChannel.MapMode.READ_WRITE, realPosition, size);
                mappedByteBuffer.put(insertBytes);
                if (force) {
                    mappedByteBuffer.force();
                }

            } catch (Exception e) {
                throw new RuntimeException("写文件出错");
            }
            if (i > 0) {
                extentPosition.add(realPosition);
            }
        }
        return extentPosition.isEmpty() ? null : extentPosition;
    }

    public List<Long> rewrite(long position, long size, byte[] content) {
        return rewrite(TestTree.url, position, size, false, content);
    }

    public List<Long> rewriteForce(long position, long size, byte[] content) {
        return rewrite(TestTree.url, position, size, true, content);
    }

    public void straightRewrite(FileChannel channel, long position, long size, boolean force, byte[] contentBytes) {
        int length = contentBytes.length;
        byte[] insertBytes = new byte[(int) size];
        System.arraycopy(contentBytes, 0, insertBytes, 0, length);
        Arrays.fill(insertBytes, length, (int) size, (byte) 32);
        try {
            MappedByteBuffer mappedByteBuffer = channel.map(FileChannel.MapMode.READ_WRITE, position, size);
            mappedByteBuffer.put(insertBytes);
            if (force) {
                mappedByteBuffer.force();
            }
        } catch (Exception e) {
            throw new RuntimeException("写文件出错");
        }
    }


    public String strToByteStr(String content) {
        StringBuilder stringBuilder = new StringBuilder();
        for (byte b : content.getBytes()) {
            stringBuilder.append(b).append(",");
        }
        return stringBuilder.toString();
    }

    public String byteStrToStr(String byteStr) {
        String[] split = byteStr.split(",");
        byte[] bytes = new byte[split.length];
        int i = 0;
        for (String s : split) {
            if (!s.trim().isEmpty()) {
                bytes[i++] = Byte.parseByte(s);
            }
        }
        return new String(bytes);
    }

    public byte[] byteStrToArr(String byteStr) {
        String[] split = byteStr.split(",");
        byte[] bytes = new byte[split.length];
        int i = 0;
        for (String s : split) {
            if (!s.trim().isEmpty()) {
                bytes[i++] = Byte.parseByte(s);
            }
        }
        return bytes;
    }

//    @Override
//    public List<Long> multiplyUseChannel(FileChannel channel, long position, long size, boolean force, byte[] contentBytes) {
//        List<Long> extentPosition = new ArrayList<>();
//        int length = contentBytes.length;
//        int n = (int) Math.ceil(length / (size + 0.));
//        for (int i = 0; i < n; i++) {
//            long realPosition = i == 0 ? position : BPlusFileInfo.allocate((int) size);
//            byte[] insertBytes = new byte[(int) size];
//            for (int k = 0; k < size; k++) {
//                int index = (int) (i * size + k);
//                insertBytes[k] = length > index ? contentBytes[index] : 32;
//            }
//            try {
//                MappedByteBuffer mappedByteBuffer = channel.map(FileChannel.MapMode.READ_WRITE, realPosition, size);
//                mappedByteBuffer.put(insertBytes);
//                if (force) {
//                    mappedByteBuffer.force();
//                }
//            } catch (Exception e) {
//                throw new RuntimeException("写文件出错");
//            }
//            if (i > 0) {
//                extentPosition.add(realPosition);
//            }
//        }
//        return extentPosition;
//    }

    private static class Singleton {
        private static final FileUtil SINGLE_TON = new FileUtil();
    }

    private FileUtil() {
    }
}
